package com.cyf.github;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author chenyifan
 * @create 2024-08-12 17:06
 */
@Component
public class SqlUtil {

    /**
     * 1000条数据 提交一次
     */
    private final static int BATCH_SIZE = 1000;

    /**
     * 最大拼接SQL数
     * 如果单条sql太长 可以调整这个数值
     * 20~50  性能最好
     */
    private final static int BATCH_CNT_SQL_SIZE = 50;

    private final SqlSessionFactory factory;

    public SqlUtil(SqlSessionFactory factory) {
        this.factory = factory;
    }


    // ----------------------------------------- 批量保存 ----------------------------------------------------

    /**
     * 批量执行 sql，每个 sql 只能执行单个数据
     * @param list  数据列表
     * @param mapperClass mapper 类
     * @param consumer  mapper 执行方法
     */
    public <E, M> boolean batchExecute(List<E> list, Class<M> mapperClass, BiConsumer<E, M> consumer) {
        SqlSession sqlSession = null;
        try {
            sqlSession = factory.openSession(ExecutorType.BATCH, false);
            M mapper = sqlSession.getMapper(mapperClass);
            int batchCount = 0;
            for (E e : list) {
                consumer.accept(e, mapper);
                batchCount++;
                if (batchCount >= BATCH_SIZE) {
                    sqlSession.flushStatements();
                    batchCount = 0;
                }
            }
            if (batchCount > 0) {
                sqlSession.flushStatements();
            }
            sqlSession.commit();
            return true;
        } catch (Throwable t) {
            sqlSession.rollback();
            throw new RuntimeException(t.getMessage());
        } finally {
            SqlSessionUtils.closeSqlSession(sqlSession, factory);
        }
    }


    /**
     * 批量保存，解决 mybatis-plus 在批量插入时由于主键自增报错问题
     * 如果主键不是自增，不要调用
     * @param list     数据集合
     * @param mClazz   mapper
     * @return 操作结果
     */
    public <E, M extends BaseMapper<E>> boolean executeSave(Class<M> mClazz, List<E> list) {
        SqlSession sqlSession = null;
        try {
            sqlSession = factory.openSession(ExecutorType.BATCH, false);
            M mapper = sqlSession.getMapper(mClazz);
            MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(mClazz.getName() + ".insert");
            MetaObject metaObject = SystemMetaObject.forObject(ms);
            metaObject.setValue("keyGenerator", NoKeyGenerator.INSTANCE);
            int batchCount = 0;
            for (E e : list) {
                mapper.insert(e);
                batchCount++;
                if (batchCount >= BATCH_SIZE) {
                    sqlSession.flushStatements();
                    batchCount = 0;
                }
            }
            // 如果还有剩余的元素未刷新，则刷新
            if (batchCount > 0) {
                sqlSession.flushStatements();
            }
            sqlSession.commit();
            return true;
        } catch (Throwable t) {
            sqlSession.rollback();
            throw new RuntimeException(t.getMessage());
        } finally {
            SqlSessionUtils.closeSqlSession(sqlSession, factory);
        }
    }

    // ----------------------------------------- in 语句参数列表转换 -------------------------------------------

    /**
     * 生成 SQL 语句中的 IN 子句的参数部分
     * @param paramList 参数list
     * @return
     * @param <T>
     */
    public <T> String inParamStr(List<T> paramList) {
        StringBuilder sb = new StringBuilder();
        for (T param : paramList) {
            if (param instanceof Number) {
                sb.append(",").append(param);
            } else {
                sb.append(",'").append(param.toString()).append("'");
            }
        }
        return sb.substring(1);
    }

    /**
     * 生成 SQL 语句中的 IN 子句的参数部分
     * @param paramList 参数list
     * @param valueMapper 参数映射
     * @return
     * @param <T>
     */
    public <T, V> String inParamStr(List<T> paramList, Function<T, V> valueMapper) {
        List<V> list = paramList.stream().map(valueMapper).collect(Collectors.toList());
        return inParamStr(list);
    }
}
